package com.css.fxfzysx.modules.vaolEngineering.service.impl;

import com.css.fxfzysx.base.response.RestResponse;
import com.css.fxfzysx.constants.FxfzConstants;
import com.css.fxfzysx.modules.lifeline.entity.newEntity.SmxMainTableEntity;
import com.css.fxfzysx.modules.vaolEngineering.entity.CpVulnerabilityEntity;
import com.css.fxfzysx.modules.vaolEngineering.param.MainTableParam;
import com.css.fxfzysx.modules.vaolEngineering.repository.PseVulnerabilityRepository;
import com.css.fxfzysx.modules.vaolEngineering.service.PseVulnerabilityService;
import com.css.fxfzysx.modules.vaolEngineering.utils.LeastSquaresRegression;
import com.css.fxfzysx.modules.vaolEngineering.utils.MainTableUtils;
import com.css.fxfzysx.modules.vaomeFacilities.utils.PinyinAPI;
import com.css.fxfzysx.util.PlatformObjectUtils;
import com.css.fxfzysx.util.UUIDGenerator;
import com.css.fxfzysx.web.PlatformSessionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
@Slf4j
public class PseVulnerabilityServiceImpl implements PseVulnerabilityService {

    @Autowired
    private PseVulnerabilityRepository pseVulnerabilityRepository;

    @Autowired
    private MainTableUtils mainTableUtils;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePseVulnerability(List<CpVulnerabilityEntity> cpVulnerabilityList, MainTableParam tableParam) {
        SmxMainTableEntity entity = new SmxMainTableEntity();
        String uuid = UUIDGenerator.getUUID();
        entity.setFortification(tableParam.getFortification());
        entity.setDeviceType(tableParam.getDeviceType());
        entity.setYear(tableParam.getYear());
        entity.setFloor(tableParam.getFloor());
        entity.setId(uuid);
        entity.setCreateUser(PlatformSessionContext.getUserID());
        entity.setBusinessObject(FxfzConstants.CZT_SMX_GD);
        entity.setCalculationStatus(1);
        mainTableUtils.saveMainTable(entity);

        if (PlatformObjectUtils.isNotEmpty(cpVulnerabilityList)) {
            cpVulnerabilityList.forEach(it -> {
                it.setProjectId(uuid);
                it.setCpvId(UUIDGenerator.getUUID());
                it.setDelFlag("0");
                pseVulnerabilityRepository.savePseVulnerability(it);
            });
        }
    }

    @Override
    public RestResponse caPseVulnerability(List<CpVulnerabilityEntity> cpVulnerabilityList) {
        try {
            if (PlatformObjectUtils.isNotEmpty(cpVulnerabilityList)) {
                cpVulnerabilityList.forEach(it -> {
                    //公式3
                    BigDecimal pai = getValue(new BigDecimal(2).multiply(new BigDecimal(Math.PI)));
                    BigDecimal divide = new BigDecimal(1).divide(it.getO().multiply(pai), 4, RoundingMode.HALF_UP);
                    BigDecimal divide1 = (new BigDecimal(it.getIntensity()).subtract(it.getU()).pow(2))
                            .divide(new BigDecimal(2).multiply(it.getO().pow(2)), 4, RoundingMode.HALF_UP).negate();
                    BigDecimal exp = BigDecimal.valueOf(Math.exp(Double.parseDouble(divide1.toString())));
                    BigDecimal resultF = divide.multiply(exp).setScale(2, RoundingMode.HALF_UP);
                    it.setFxexp(resultF.setScale(4, RoundingMode.HALF_UP));
                    it.setFxexpCorrect(it.getFxexp());
                    //公式2
                    BigDecimal divide2 = (new BigDecimal(it.getIntensity()).subtract(it.getU())).divide(it.getO().multiply(pai), 4, RoundingMode.HALF_UP);
                    BigDecimal resultBigF = new BigDecimal("0.5").multiply(BigDecimal.valueOf(erf(Double.parseDouble(divide2.toString()))));
                    it.setFxerf(resultBigF.setScale(4, RoundingMode.HALF_UP));
                    //公式1
                    BigDecimal negate = new BigDecimal(it.getIntensity().toString()).subtract(it.getU())
                            .divide(it.getO().multiply(getValue(new BigDecimal(2))), 4, RoundingMode.HALF_UP).negate().pow(2);
                    BigDecimal resultErf = BigDecimal.valueOf(0.5).subtract(BigDecimal.valueOf(Math.exp(negate.doubleValue()) - 1)
                            .divide(new BigDecimal(2).multiply(getValue(new BigDecimal(Math.PI))), 4, RoundingMode.HALF_UP));
                    it.setErf(resultErf);
                });
            }
            return RestResponse.succeed(cpVulnerabilityList);
        } catch (Exception e) {
            String errorMessage = "数据异常,计算失败!";
            log.error(errorMessage, e);
            return RestResponse.fail(errorMessage);
        }
    }

    //erf函数
    public static double erf(double x) {
        double result = 0;
        int index = 0;
        do {
            index++;
        } while (x / Math.pow(10, index) > 1e-3);//设置计算精度
        int maxIndex = (int) Math.pow(10, index);
        double deltaX = x / maxIndex;
        for (int i = 0; i <= maxIndex; i++) {
            if (i > 0 && i < maxIndex) {
                result += 2 * Math.exp(-Math.pow(deltaX * i, 2));
            } else if (i == maxIndex) {
                result += Math.exp(-Math.pow(deltaX * i, 2));
            } else if (i == 0) {
                result += Math.exp(-Math.pow(deltaX * i, 2));
            }
        }
        return result * deltaX / Math.pow(Math.PI, 0.5);
    }


    /**
     * 开平方根
     *
     * @param value
     * @return
     */
    private static BigDecimal getValue(BigDecimal value) {
        BigDecimal num2 = BigDecimal.valueOf(2);
        int precision = 100;
        MathContext mc = new MathContext(precision, RoundingMode.HALF_UP);
        BigDecimal deviation = value;
        int cnt = 0;
        while (cnt < precision) {
            deviation = (deviation.add(value.divide(deviation, mc))).divide(num2, mc);
            cnt++;
        }
        deviation = deviation.setScale(7, BigDecimal.ROUND_HALF_UP);
        return deviation;
    }

    @Override
    public RestResponse generateCurve(String projectId) {
        try {
            List<CpVulnerabilityEntity> list = pseVulnerabilityRepository.queryPseVulnerabilityById(projectId);
            String[] type = {"BYQ", "DLQ", "GLKG", "BLQ", "DLHGQ", "DYHGQ"};
            HashMap<String, Object> threadMap = getGenerateCurveValue(list, type);
            return RestResponse.succeed(threadMap);
        } catch (Exception e) {
            String errorMessage = "失败!";
            log.error(errorMessage, e);
            return RestResponse.fail(errorMessage);
        }
    }

    /**
     * 获取计算出来的曲线值
     *
     * @param list
     * @param types
     * @return
     */
    private HashMap<String, Object> getGenerateCurveValue(List<CpVulnerabilityEntity> list, String[] types) {
        HashMap<String, Object> resultMap = new HashMap<>();
        HashMap<String, Object> threadMap = new HashMap<>();
        HashMap<String, Object> dropMap = new HashMap<>();
        ArrayList<Integer> x = new ArrayList<>();
        ArrayList<Double> y = new ArrayList<>();
        for (String type : types) {
            HashMap<Integer, Object> dropDataMap = new HashMap<>();
            list.forEach(it -> {
                if (PinyinAPI.getPinYinHeadChar(it.getEquipmentType()).contains(type)) {
                    dropDataMap.put(it.getIntensity(), it.getFxexp());
                    x.add(it.getIntensity());
                    y.add(Double.valueOf(it.getFxexp().toString()));
                }
            });
            Integer[] xData = x.toArray(new Integer[x.size()]);
            Double[] yData = y.toArray(new Double[y.size()]);
            Integer[] preData = new Integer[]{6, 7, 8, 9, 10, 11};
            HashMap<Integer, Double> map = LeastSquaresRegression.leastSquareMethod(xData, yData, preData);
            threadMap.put(type, map);
            dropMap.put(type, dropDataMap);
        }
        resultMap.put("threadMap", threadMap);
        resultMap.put("dropMap", dropMap);
        return resultMap;
    }

    @Override
    public void updateMainTable(MainTableParam mainTableParam) {
        pseVulnerabilityRepository.updateMainTable(mainTableParam);
    }

    @Override
    public void updateValue(CpVulnerabilityEntity it) {
        pseVulnerabilityRepository.updateValue(it);
    }

    @Override
    public List<CpVulnerabilityEntity> getByProjectId(String id) {
        return pseVulnerabilityRepository.getByProjectId(id);
    }
}
